﻿namespace MyCoreLib.UnitTest.Data.Common
{
    using System;
    using System.Collections.Generic;
    using System.Data;
    using System.Data.Fakes;

    using Microsoft.VisualStudio.TestTools.UnitTesting;

    using MyCoreLib.Data.Common.Fakes;

    [TestClass]
    public class DbClientObject_OpenConnection_Should
    {
        [TestMethod]
        public void OpenInnerConnectionOnFirstCall()
        {
            bool isOpen = false;
            var connection = new StubIDbConnection { Open = () => isOpen = true };
            var client = new StubDbClientObject(connection);
            using (client.OpenConnection())
            {
                Assert.IsTrue(isOpen, "IDbConnection.Open should be called on the inner connection object.");
            }
        }

        [TestMethod]
        public void OpenInnerConnectionAtMostOnce()
        {
            const int SUBSEQUENT_OPEN_CALLS_COUNT = 10;
            int openCount = 0;
            int closeCount = 0;

            var connection = new StubIDbConnection
                                 {
                                     Open = () => openCount++,
                                     Close = () => 
                                     { 
                                         closeCount++;
                                         openCount--;
                                     },
                                     StateGet = () => openCount > 0 ? ConnectionState.Open : ConnectionState.Closed
                                 };

            var client = new StubDbClientObject(connection);

            using (client.OpenConnection())
            {
                var subsequentOpenDisposables = new Stack<IDisposable>();

                for (int i = 0; i < SUBSEQUENT_OPEN_CALLS_COUNT; ++i)
                {
                    subsequentOpenDisposables.Push(client.OpenConnection());
                    Assert.AreEqual(1, openCount, "Subsequent ConnectionOpen calls should not be calling IDbConnection.Open. Subsequent call number = {0}", i);
                }

                do
                {
                    subsequentOpenDisposables.Pop().Dispose();
                    Assert.AreEqual(1, openCount, "Disposing subsequent open-connection-disposables should keep the actual connection open.");
                }
                while (subsequentOpenDisposables.Count > 0);
            }

            Assert.AreEqual(0, openCount, "After the root open-connection-disposable is disposed, the actual connection should be closed.");
            Assert.AreEqual(1, closeCount, "IDbConnection.Close should be called at most once.");
        }
    }
}
